package main

import (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/morgine/log"
	"github.com/morgine/pkg/config"
	"github.com/morgine/pkg/redis"
	"github.com/shopspring/decimal"
	"io/ioutil"
	"market/analysis"
	"market/internal"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"
	"time"
)

type Configs struct {
	Coins []string `toml:"coins"`
}

func main() {
	// 加载配置文件
	configFile := flag.String("c", "config.toml", "配置文件")
	flag.Parse()

	//savePrice(&Price{})
	// 初始化配置服务
	configs, err := config.UnmarshalFile(*configFile)
	if err != nil {
		panic(err)
	}
	client, err := redis.NewClient("redis", configs)
	if err != nil {
		panic(err)
	}
	defer client.Close()

	cfgs := &Configs{}

	err = configs.UnmarshalSub("subscribe", cfgs)
	if err != nil {
		panic(err)
	}
	storage := internal.NewRedisStorage(client)
	var maxTimes float64 = 60
	var maxLasts int = 20
	var originUSDT = decimal.New(1000, 0)
	var priceUSDT = decimal.New(1005, 0)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	//ctx2, cancel2 := context.WithCancel(context.Background())
	//defer cancel2()
	dataDir := "data"
	go func() {
		err = os.MkdirAll(dataDir, os.ModePerm)
		if err != nil {
			panic(err)
		}
		for _, coin := range cfgs.Coins {
			coinDir := filepath.Join(dataDir, coin)
			err = os.MkdirAll(coinDir, os.ModePerm)
			if err != nil {
				panic(err)
			}
			flows, err := storage.TotalFlows(coin, 86400)
			if err != nil {
				panic(err)
			}
			log.Info.Printf("starting analysis %s, total flows %d...\n", coin, len(flows))
			for bt := float64(1); bt <= maxTimes; bt += 2 {
				percent := int(100 * bt / maxTimes)
				log.Info.Printf("%s analysis %d...\n", coin, percent)
				for bl := 1; bl <= maxLasts; bl += 2 {
					for st := float64(1); st <= maxTimes; st += 2 {
						//for sl := 1; sl < maxLasts; sl++ {
						sl := 1
						for sl <= maxLasts {
							select {
							case <-ctx.Done():
								return
							default:
								ai := 1
								for ai <= 900 {
									select {
									case <-ctx.Done():
										return
									default:
										strategy := &analysis.TimesStrategy{
											Buy: &analysis.TimesOption{
												Times: bt,
												Lasts: bl,
											},
											Sell: &analysis.TimesOption{
												Times: st,
												Lasts: sl,
											},
										}
										player := analysis.NewPlayer(strategy, originUSDT)
										avgBuySize := analysis.NewAverageCalculator(ai)
										avgSellSize := analysis.NewAverageCalculator(ai)
										longBuySize := analysis.NewAverageCalculator(ai * 4)
										longSellSize := analysis.NewAverageCalculator(ai * 4)
										analysedFlows := 0
										var lastStatisticFlow *internal.Flow
										totalFLows := len(flows)
										//time.Sleep(40 * time.Microsecond)
										func() {
											for fi, flow := range flows {
												avgBuySize.Append(flow.BuySize)
												avgSellSize.Append(flow.SellSize)
												longBuySize.Append(flow.BuySize)
												longSellSize.Append(flow.SellSize)
												if longSellSize.IsFull() {
													analysedFlows++
													lastStatisticFlow = flow
													player.Play(flow, avgBuySize.GetAverage(), avgSellSize.GetAverage(), longBuySize.GetAverage(), longSellSize.GetAverage())
													if (player.LenOrders() / 10) > (fi / totalFLows) {
														return
													}
												}
											}
											if player.GetTotalUSDT().GreaterThan(priceUSDT) {
												price := &Price{
													Coin:           coin,
													Strategy:       strategy,
													Orders:         player.GetOrders(),
													AverageSeconds: ai,
													TotalUSDT:      player.GetTotalUSDT(),
													StatisticFlows: analysedFlows,
													LastFlow:       lastStatisticFlow,
												}
												savePrice(coinDir, price)
											}
										}()

										ai *= 2
									}
								}

								sl += 2
							}
						}
					}
				}
			}
		}
	}()
	quit := make(chan os.Signal)
	// kill (no param) default send syscall.SIGTERM
	// kill -2 is syscall.SIGINT
	// kill -9 is syscall.SIGKILL but can't be catch, so don't need add it
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
	log.Init.Println("shutting down...")
	defer func() {
		log.Info.Println("done")
	}()
}

type Price struct {
	Coin           string
	Strategy       *analysis.TimesStrategy
	Orders         []*analysis.PlayerOrder
	AverageSeconds int
	TotalUSDT      decimal.Decimal
	StatisticFlows int
	LastFlow       *internal.Flow
}

func savePrice(dir string, p *Price) {
	data, err := json.Marshal(p)
	if err != nil {
		panic(err)
	}
	t := time.Unix(p.LastFlow.Timestamp, 0).Format("01-02-15-04-05")
	sb := p.Strategy.Buy
	ss := p.Strategy.Sell
	totalUSDT := p.TotalUSDT.IntPart()
	err = ioutil.WriteFile(filepath.Join(dir, fmt.Sprintf("%05d-%05d-%05d-%05d-%05d-%05d--%s.json", totalUSDT, int(sb.Times), sb.Lasts, int(ss.Times), ss.Lasts, p.AverageSeconds, t)), data, os.ModePerm)
	if err != nil {
		panic(err)
	}
}
